<?php

namespace Hx;

/**
 * 路由类
 * 负责路由器的处理和分发
 * Class Router
 * @package Hx
 */
class Router {

    /**
     * 原始路由规则
     * @var array
     */
    private $rawRules;

    /**
     * 解析后路由规则
     * @var array
     */
    private $realRules;

    /**
     * 是否忽略匹配后缀名
     * @var bool
     */
    private $ignoreExt = FALSE;

    /**
     * 路由类构造
     * @param array $config 初始配置，来自于配置文件或自定义数组
     */
    public function __construct($config) {
        $this->rawRules = $config['rules'];
        #解析规则
        foreach ($config['rules'] as $rule) {
            $this->realRules[] = $this->parseRule($rule);
        }
        $this->ignoreExt = (bool)$config['ignoreExt'];
    }

    /**
     * 解析规则
     * @param string $rule
     * @return array
     */
    private function parseRule($rule) {
        $rule = explode(' | ', $rule);
        #请求方式
        $return['method'] = strtoupper($rule[0]);
        #后缀名
        $ext = pathinfo($rule[1], PATHINFO_EXTENSION);
        if ($ext) {
            $rule[1] = str_replace('.' . $ext, '', $rule[1]);
            $return['ext'] = $ext;
        } else {
            $return['ext'] = 'html';
        }
        #路由参数
        $return['param'] = array_values(array_filter(explode('/', $rule[1])));
        #回调
        $return['callback'] = explode('->', $rule[2]);
        return $return;
    }

    /**
     * 当前请求的参数
     * @var array
     */
    private $currentRule;

    /**
     * 分析请求URL得到参数
     * @param string $url
     * @return array
     */
    private function parseMapUrl($url) {
        #请求方式
        $method = isset($_SERVER['HTTP_METHOD']) ? strtoupper($_SERVER['HTTP_METHOD']) : strtoupper($_SERVER['REQUEST_METHOD']);
        if (!$method) {
            $method = 'GET';
        }
        #后缀名
        $ext = pathinfo($url, PATHINFO_EXTENSION);
        if ($ext) {
            $url = str_replace('.' . $ext, '', $url);
        } else {
            $ext = 'html';
        }
        #参数值
        $param = array_values(array_filter(explode('/', $url)));
        return array(
            'method' => $method,
            'ext'    => $ext,
            'param'  => $param
        );
    }

    /**
     * 匹配的规则，解析后的结果
     * @var array
     */
    private $matchRule;

    /**
     * 匹配规则
     * @param array $userRule 被匹配的对象
     * @return array|bool
     */
    private function mapRule($userRule) {
        foreach ($this->realRules as $rule) {
            #参数是否合适
            if (count($rule['param']) != count($userRule['param'])) {
                continue;
            }
            #后缀名是否正确
            if ($rule['ext'] != $userRule['ext'] && $this->ignoreExt === FALSE) {
                continue;
            }
            #请求方式是否一致
            if ($rule['method'] != $userRule['method']) {
                continue;
            }
            #计算参数差值
            $diff = array_diff($userRule['param'], $rule['param']);
            $diffArg = array_diff($rule['param'], $userRule['param']);
            $argData = array();
            #比较参数差值
            foreach ($diffArg as $k => $arg) {
                #如果有非*项，匹配失败
                if ($arg !== '*') {
                    continue 2;
                }
                #如果有*项，但位置不对，匹配失败
                if (!$diff[$k]) {
                    continue 2;
                }
                $argData[] = $diff[$k];
            }
            $this->matchRule = $rule;
            return array('callback' => $rule['callback'], 'args' => $argData);
        }
        return FALSE;
    }

    /**
     * 分发路由
     * @param string $url 请求的URL
     * @return array|bool
     */
    public function map($url) {
        $this->currentRule = $this->parseMapUrl($url);
        return $this->mapRule($this->currentRule);
    }

    //---------------

    /**
     * 获取原始规则
     * @return array
     */
    public function getRaw() {
        return $this->rawRules;
    }

    /**
     * 获取解析规则
     * @return array
     */
    public function getRules() {
        return $this->realRules;
    }

    /**
     * 获取命名的规则
     * @param string $name
     * @return array
     */
    public function getRule($name) {
        return $this->realRules[$name];
    }

    /**
     * 获取匹配的规则
     * @return array
     */
    public function getMatched() {
        return $this->matchRule;
    }

    /**
     * 获取当前URL解析结果
     * @return array
     */
    public function getCurrent() {
        return $this->currentRule;
    }

}